package com.matrix.marketing.service.impl;

import com.matrix.api.marketing.MarketingApi;
import com.matrix.api.marketing.dto.DynamicCouponGrantDTO;
import com.matrix.api.marketing.dto.RefundCalculationRequestDTO;
import com.matrix.api.marketing.vo.RefundCalculationResultVO;
import com.matrix.api.marketing.vo.UserCouponVO;
import com.matrix.common.enums.CouponStatusEnum;
import com.matrix.common.exception.BusinessException;
import com.matrix.marketing.constant.MarketingConstants;
import com.matrix.marketing.converter.MarketingConverter;
import com.matrix.marketing.entity.CouponTemplate;
import com.matrix.marketing.entity.PointsLog;
import com.matrix.marketing.entity.UserCoupon;
import com.matrix.marketing.entity.UserPoints;
import com.matrix.marketing.mapper.UserCouponMapper;
import com.matrix.marketing.service.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;

/**
 * @author 有点甜
 * @since 2025/8/1
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class MarketingInternalServiceImpl implements MarketingApi {

    private final UserCouponService userCouponService;
    private final CouponTemplateService couponTemplateService;
    private final UserPointsService userPointsService;
    private final PointsLogService pointsLogService;
    private final UserCouponMapper userCouponMapper;

    private static final int POINTS_PER_YUAN = 10;

    @Override
    @Transactional
    public void grantNewUserCoupon(Long userId) {
        long newUserTemplateId = MarketingConstants.NEW_USER_COUPON_TEMPLATE_ID;
        CouponTemplate template = couponTemplateService.getById(newUserTemplateId);
        if (template != null) {
            UserCoupon userCoupon = new UserCoupon();
            userCoupon.setUserId(userId);
            userCoupon.setTemplateId(newUserTemplateId);
            userCoupon.setObtainTime(LocalDateTime.now());
            userCoupon.setExpireTime(LocalDateTime.now().plusDays(template.getValidDays()));
            userCoupon.setStatus(CouponStatusEnum.UNUSED);
            userCouponService.save(userCoupon);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public UserCouponVO grantDynamicCouponByMerchant(DynamicCouponGrantDTO dto) {
        if (dto.getDiscountAmount().compareTo(BigDecimal.ZERO) <= 0) {
            throw new BusinessException("抵扣金额必须大于0");
        }
        if (dto.getCouponType() == DynamicCouponGrantDTO.CouponType.THRESHOLD &&
                dto.getThresholdAmount().compareTo(dto.getDiscountAmount()) <= 0) {
            throw new BusinessException("满减门槛必须大于抵扣金额");
        }

        CouponTemplate template = new CouponTemplate();
        template.setIssuerType(2);
        template.setIssuerId(dto.getShopId());
        template.setTitle(dto.getTitle());

        if (dto.getCouponType() == DynamicCouponGrantDTO.CouponType.NO_THRESHOLD) {
            template.setCouponType(3);
            template.setThresholdAmount(BigDecimal.ZERO);
        } else {
            template.setCouponType(1);
            template.setThresholdAmount(dto.getThresholdAmount());
        }

        template.setDiscountAmount(dto.getDiscountAmount());
        template.setTotalQuantity(1);
        template.setIssuedQuantity(1);
        template.setValidityType(1);
        template.setValidDays(30);
        template.setStatus(2);

        couponTemplateService.save(template);
        log.info("Dynamically created coupon template {} for shop {}", template.getId(), dto.getShopId());

        UserCoupon userCoupon = new UserCoupon();
        userCoupon.setUserId(dto.getTargetUserId());
        userCoupon.setTemplateId(template.getId());
        userCoupon.setStatus(CouponStatusEnum.UNUSED);
        userCoupon.setObtainTime(LocalDateTime.now());
        userCoupon.setExpireTime(LocalDateTime.now().plusDays(template.getValidDays()));
        userCouponService.save(userCoupon);
        log.info("Granted dynamic coupon (templateId: {}) to user {}", template.getId(), dto.getTargetUserId());

        return MarketingConverter.INSTANCE.toUserCouponVO(userCoupon, template);
    }

    @Override
    @Transactional
    public void addPointsForOrder(Long userId, BigDecimal orderAmount, String orderId) {
        int pointsToAdd = orderAmount.intValue();
        if (pointsToAdd <= 0) {
            return;
        }

        UserPoints userPoints = userPointsService.lambdaQuery().eq(UserPoints::getUserId, userId).one();
        if (userPoints == null) {
            userPoints = new UserPoints();
            userPoints.setUserId(userId);
            userPoints.setTotalPoints((long) pointsToAdd);
            userPointsService.save(userPoints);
        } else {
            userPoints.setTotalPoints(userPoints.getTotalPoints() + pointsToAdd);
            if (!userPointsService.updateById(userPoints)) {
                throw new BusinessException("更新积分失败,请重试");
            }
        }

        PointsLog log = new PointsLog();
        log.setUserId(userId);
        log.setPointsChange(pointsToAdd);
        log.setCurrentPoints(userPoints.getTotalPoints());
        log.setSourceType(1);
        log.setSourceId(orderId);
        log.setDescription("订单消费获得积分");
        pointsLogService.save(log);
    }

    @Override
    @Transactional
    public BigDecimal useCoupon(Long userId, Long userCouponId, Long orderId) {
        if (userCouponId == null || userCouponId <= 0) {
            return BigDecimal.ZERO;
        }
        UserCoupon userCoupon = userCouponMapper.selectByIdForUpdate(userCouponId);

        if (userCoupon == null || !userCoupon.getUserId().equals(userId)) {
            throw new BusinessException("优惠券不存在");
        }
        if (userCoupon.getStatus() != CouponStatusEnum.UNUSED) {
            throw new BusinessException("优惠券已被使用或已过期");
        }
        if (userCoupon.getExpireTime().isBefore(LocalDateTime.now())) {
            userCoupon.setStatus(CouponStatusEnum.EXPIRED);
            userCouponService.updateById(userCoupon);
            throw new BusinessException("优惠券已过期");
        }

        userCoupon.setStatus(CouponStatusEnum.UNUSED);
        userCoupon.setUsedTime(LocalDateTime.now());
        userCoupon.setUsedOrderId(orderId);
        userCouponService.updateById(userCoupon);

        CouponTemplate template = couponTemplateService.getById(userCoupon.getTemplateId());
        if (template == null) {
            throw new BusinessException("优惠券模板数据异常");
        }
        return template.getDiscountAmount();
    }

    @Override
    public List<UserCouponVO> getMyCoupons(Long userId, Long shopId) {
        return userCouponMapper.findAvailableCoupons(userId, shopId, LocalDateTime.now());
    }

    @Override
    @Transactional
    public void updateCouponOrderId(Long userCouponId, Long orderId) {
        if (userCouponId == null || userCouponId <= 0 || orderId == null || orderId <= 0) {
            log.warn("无效的参数. userCouponId: {}, orderId: {}", userCouponId, orderId);
            return;
        }

        UserCoupon userCoupon = userCouponService.getById(userCouponId);
        if (userCoupon == null) {
            log.error("严重错误:尝试为不存在的优惠券 {} 更新订单ID {}", userCouponId, orderId);
            throw new BusinessException("内部数据错误:优惠券不存在");
        }
        
        if (orderId.equals(userCoupon.getUsedOrderId())) {
            log.info("优惠券 {} 已关联订单 {}, 无需重复更新", userCouponId, orderId);
            return;
        }
        
        if (userCoupon.getUsedOrderId() != -1L) {
            log.error("严重错误:优惠券 {} 状态异常，其 orderId 为 {}，但尝试更新为 {}", userCouponId, userCoupon.getUsedOrderId(), orderId);
            throw new BusinessException("优惠券状态冲突");
        }
        
        UserCoupon updateEntity = new UserCoupon();
        updateEntity.setId(userCouponId);
        updateEntity.setUsedOrderId(orderId);
        userCouponService.updateById(updateEntity);

        log.info("成功将优惠券 {} 与订单 {} 关联", userCouponId, orderId);
    }
    
    @Override
    @Transactional
    public void releaseCoupon(Long userCouponId) {
        UserCoupon userCoupon = userCouponService.getById(userCouponId);
        if (userCoupon != null && userCoupon.getStatus() == CouponStatusEnum.USED) {
            userCoupon.setStatus(CouponStatusEnum.UNUSED);
            userCoupon.setUsedTime(null);
            userCoupon.setUsedOrderId(null);
            userCouponService.updateById(userCoupon);
            log.info("已成功返还优惠券: {}", userCouponId);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public RefundCalculationResultVO processCouponForRefund(RefundCalculationRequestDTO request) {
        BigDecimal P = request.getCanceledItemPrice();
        BigDecimal C = request.getPlatformCouponDiscount() != null ? request.getPlatformCouponDiscount() : BigDecimal.ZERO;
        BigDecimal M = request.getMerchantCouponDiscount() != null ? request.getMerchantCouponDiscount() : BigDecimal.ZERO;

        boolean hasPlatformCoupon = request.getPlatformCouponId() != null && request.getPlatformCouponId() > 0;
        boolean hasMerchantCoupon = request.getMerchantCouponId() != null && request.getMerchantCouponId() > 0;

        String sourceId = "order:" + request.getOrderId();

        if (hasPlatformCoupon && !hasMerchantCoupon) {
            if (P.compareTo(C) <= 0) {
                int points = calculatePointsFromAmount(P);
                addUserPoints(request.getUserId(), points, "退菜返还积分", 2, sourceId);
                return new RefundCalculationResultVO(BigDecimal.ZERO, points, "场景1.1: 退菜价P<=平台券C, P元转积分。");
            } else {
                int points = calculatePointsFromAmount(C);
                addUserPoints(request.getUserId(), points, "优惠券作废返还积分", 2, sourceId);
                return new RefundCalculationResultVO(P.subtract(C), points, "场景1.2: 退菜价P>平台券C, C元转积分, 退现金P-C。");
            }
        } else if (!hasPlatformCoupon && hasMerchantCoupon) {
            if (P.compareTo(M) < 0) {
                return new RefundCalculationResultVO(BigDecimal.ZERO, 0, "场景2.1: 退菜价P<商家券M, 不退券不退款。");
            } else {
                releaseCoupon(request.getMerchantCouponId());
                return new RefundCalculationResultVO(P.subtract(M), 0, "场景2.2: 退菜价P>=商家券M, 返还商家券, 退现金P-M。");
            }
        } else if (hasPlatformCoupon && hasMerchantCoupon) {
            if (P.compareTo(M) < 0) {
                return new RefundCalculationResultVO(BigDecimal.ZERO, 0, "场景3.1: P<M, 不退券不退款。");
            } else if (P.compareTo(M) >= 0 && P.compareTo(C.add(M)) < 0) {
                releaseCoupon(request.getMerchantCouponId());
                int points = calculatePointsFromAmount(P.subtract(M));
                addUserPoints(request.getUserId(), points, "退菜返还积分", 2, sourceId);
                return new RefundCalculationResultVO(BigDecimal.ZERO, points, "场景3.2: M<=P<C+M, 返还商家券, P-M部分转积分。");
            } else {
                releaseCoupon(request.getMerchantCouponId());
                int points = calculatePointsFromAmount(C);
                addUserPoints(request.getUserId(), points, "优惠券作废返还积分", 2, sourceId);
                return new RefundCalculationResultVO(P.subtract(C).subtract(M), points, "场景3.3: P>=C+M, 返还商家券, C元转积分, 退现金P-C-M。");
            }
        } else if (!hasPlatformCoupon && !hasMerchantCoupon) {
            return new RefundCalculationResultVO(P, 0, "无优惠券场景, 全额退款。");
        }
        else {
            log.error("CRITICAL: Unhandled case in refund calculation logic! Request: {}", request);
            throw new IllegalStateException("无法处理的退款计算逻辑");
        }
    }

    @Override
    public BigDecimal getCouponDiscount(Long userCouponId) {
        if (userCouponId == null || userCouponId <= 0) {
            return BigDecimal.ZERO;
        }
        UserCoupon userCoupon = userCouponService.getById(userCouponId);
        if (userCoupon == null) {
            return BigDecimal.ZERO;
        }
        CouponTemplate template = couponTemplateService.getById(userCoupon.getTemplateId());
        if (template == null) {
            return BigDecimal.ZERO;
        }
        return template.getDiscountAmount();
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void addUserPoints(Long userId, int pointsToAdd, String description, int sourceType, String sourceId) {
        if (pointsToAdd <= 0) {
            log.warn("Attempted to add non-positive points ({}) for user {}. Skipping.", pointsToAdd, userId);
            return;
        }

        UserPoints userPoints = userPointsService.lambdaQuery().eq(UserPoints::getUserId, userId).one();
        if (userPoints == null) {
            userPoints = new UserPoints();
            userPoints.setUserId(userId);
            userPoints.setTotalPoints((long) pointsToAdd);
            userPoints.setVersion(0);
            userPointsService.save(userPoints);
        } else {
            userPoints.setTotalPoints(userPoints.getTotalPoints() + pointsToAdd);
            if (!userPointsService.updateById(userPoints)) {
                throw new BusinessException("更新用户积分失败,可能存在并发冲突,请重试。");
            }
        }

        PointsLog logEntry = new PointsLog();
        logEntry.setUserId(userId);
        logEntry.setPointsChange(pointsToAdd);
        logEntry.setCurrentPoints(userPoints.getTotalPoints());
        logEntry.setSourceType(sourceType);
        logEntry.setSourceId(sourceId);
        logEntry.setDescription(description);
        pointsLogService.save(logEntry);

        log.info("Successfully added {} points to user {}. Reason: {}", pointsToAdd, userId, description);
    }

    private int calculatePointsFromAmount(BigDecimal amount) {
        if (amount == null || amount.compareTo(BigDecimal.ZERO) <= 0) {
            return 0;
        }
        return amount.multiply(new BigDecimal(POINTS_PER_YUAN)).intValue();
    }
}
